package com.nexgo.payment.pack;

import android.text.TextUtils;
import android.util.Base64;

import com.nexgo.common.ByteUtils;
import com.nexgo.common.TlvUtils;
import com.nexgo.data.entity.SendData;
import com.nexgo.data.entity.SendDataBuilder;
import com.nexgo.payment.App;
import com.nexgo.payment.constrant.OfflineState;
import com.nexgo.payment.constrant.PreferencesConstants;
import com.nexgo.payment.constrant.SysConstant;
import com.nexgo.payment.constrant.TransType;
import com.nexgo.payment.databases.DataSource;
import com.nexgo.payment.databases.Signature;
import com.nexgo.payment.databases.TransRecord;
import com.nexgo.payment.entity.TransData;
import com.nexgo.payment.util.MessageHelper;
import com.nexgo.payment.util.StringUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import java.util.List;

import java8.util.Optional;

/**
 * Created by xiaox on 2017/9/15.
 */

class UploadEsBuilder extends DataBuilder {
    private final Logger log;
    private TransRecord mTransRecord;
    private TransType mOrgTransType;
    private Signature mSignature;

    UploadEsBuilder(TransData transData, DataSource repository) {
        super(transData, repository);
        log = LoggerFactory.getLogger(getClass().getSimpleName());
        mTransRecord = mRepository.queryTransRecord(mTransData.getOrgTraceNum());
        mOrgTransType = TransType.values()[mTransRecord.getTransType()];
        mSignature = mRepository.getSignature(mTransRecord.getTraceNum());
    }

    @Override
    SendData form() {
        SendData sendData = new SendDataBuilder().setListener(mListener).setSignListener(mSignListener).createSendData();
        sendData.setHeader(MessageHelper.formMessageHeader(mRepository.getParamValue(PreferencesConstants.TPDU)));
        sendData.setMsgType(mTransData.getTransType().getMsgType());
        sendData.setPan(mTransRecord.getPan());
        sendData.setAmount(mTransRecord.getAmount());
        sendData.setTraceNum(mTransRecord.getTraceNum());
//        sendData.setSettleDate(mTransRecord.getSettleDate());
        sendData.setRefNum(mTransRecord.getRefNum());
        sendData.setEmvTlv(formTlv());
        sendData.setTerminalId(mRepository.getParamValue(PreferencesConstants.TERMINAL_ID));
        sendData.setMerchantId(mRepository.getParamValue(PreferencesConstants.MERCHANT_ID));
        sendData.setField60(formField60());
        sendData.setField62(formField62());
        sendData.setMac("0000000000000000");
        return sendData;
    }


    private String formField62() {
        return ByteUtils.byteArray2HexString(Base64.decode(mSignature.getSignatureData(), Base64.DEFAULT));
    }

    private String formTlv() {
        StringBuilder sb = new StringBuilder();
        //一、交易通用信息（以下域均指原交易应答报文）
        //FF00 商户名称
        formTlv(sb, "FF00", Optional.ofNullable(mRepository.getParamValue(PreferencesConstants.MERCHANT_NAME))
                .filter(s -> TextUtils.isEmpty(s))
                .map(s -> {
                    try {
                        return s.getBytes(SysConstant.GBK);
                    } catch (UnsupportedEncodingException e1) {
                        return new byte[0];
                    }
                })
                .orElse(new byte[0]));
        //FF01 交易名称
        try {
            formTlv(sb, "FF01", App.sContext.getString(mOrgTransType.getName()).getBytes(SysConstant.GBK));
        } catch (UnsupportedEncodingException e) {
            formTlv(sb, "FF01", new byte[0]);
        }
        //FF02 操作员号
        formTlv(sb, "FF02", new byte[]{(byte) mTransRecord.getOperator()});
        //FF03 收单行
        Optional.ofNullable(mTransRecord.getAcqCode())
                .map(s -> StringUtils.rightPad(s, 11, ' '))
                .map(s -> ByteUtils.byteArray2HexString(s.getBytes()))
                .ifPresent(s -> formTlv(sb, "FF03", ByteUtils.hexString2ByteArray(s)));
        //FF04 发卡行
        Optional.ofNullable(mTransRecord.getIssCode())
                .map(s -> StringUtils.rightPad(s, 11, ' '))
                .map(s -> ByteUtils.byteArray2HexString(s.getBytes()))
                .ifPresent(s -> formTlv(sb, "FF04", ByteUtils.hexString2ByteArray(s)));
        //FF05 有效期
        Optional.ofNullable(mTransRecord.getExpDate()).ifPresent(s -> formTlv(sb, "FF05", ByteUtils.hexString2ByteArray(s)));
        //FF06 交易日期
        formTlv(sb, "FF06", ByteUtils.hexString2ByteArray(StringUtils.getCalenderField(Calendar.YEAR) + mTransRecord.getDate() + mTransRecord.getTime()));
        //FF07 授权码
        Optional.ofNullable(mTransRecord.getAuthCode())
                .map(s -> ByteUtils.byteArray2HexString(s.getBytes()))
                .ifPresent(s -> formTlv(sb, "FF07", ByteUtils.hexString2ByteArray(s)));
        //FF08 小费金额 在追加小费交易时出现，包含12位数字
        //FF09卡组织通知交易和离线交易出现
        if (mTransRecord.getIsNotify() || OfflineState.APPROVE.equals(OfflineState.values()[mTransRecord.getOfflineState()])) {
            Optional.ofNullable(mTransRecord.getInterOrgCode()).ifPresent(s -> formTlv(sb, "FF09", ByteUtils.hexString2ByteArray(s)));
        }
        //FF0A 货币代码
        Optional.ofNullable(mTransRecord.getFundType())
                .map(s -> ByteUtils.byteArray2HexString(s.getBytes()))
                .ifPresent(s -> formTlv(sb, "FF0A", ByteUtils.hexString2ByteArray(s)));
        //FF0B  持卡人手机号码	CN，6    终端  C，可以获取时上送
        Optional.ofNullable(mSignature.getPhone()).ifPresent(s -> formTlv(sb, "FF0B", ByteUtils.hexString2ByteArray(s)));
        //二、IC卡有关信息（以下域均指原交易请求报文）
        //FF30  应用标签    ans,var.up to,16    PBOC借/贷记卡	C，IC卡交易获取时出现
        Optional.ofNullable(getValue(mTransRecord.getResultTlv(), "50")).ifPresent(s -> formTlv(sb, "FF30", ByteUtils.hexString2ByteArray(s)));
        //FF31  应用名称    ans,var.up to,16    PBOC借/贷记卡	C，IC卡交易获取时出现
        Optional.ofNullable(getValue(mTransRecord.getResultTlv(), "9F12")).ifPresent(s -> formTlv(sb, "FF31", ByteUtils.hexString2ByteArray(s)));
        //FF22  应用标识    B,var.up to,16  PBOC借/贷记卡	C，IC卡交易获取时出现
        Optional.ofNullable(getValue(mTransRecord.getResultTlv(), "9F06")).ifPresent(s -> formTlv(sb, "FF22", ByteUtils.hexString2ByteArray(s)));
        //FF23  应用密文    B,8 PBOC借/贷记卡	C，当存在时出现，基于借贷记应用的小额支付时表示TC，基于借贷记应用的联机交易时表示ARQC
        Optional.ofNullable(getValue(mTransRecord.getResultTlv(), "9F26")).ifPresent(s -> formTlv(sb, "FF23", ByteUtils.hexString2ByteArray(s)));
        //FF24      充值后卡片余额   CN,6    PBOC借/贷记卡	C，IC卡圈存类交易出现，包含12位数字
        //FF25      转入卡卡号	cn,var. up to 10        62域	C，当IC卡非指定帐户圈存时出现（不需要卡号屏蔽），
        if (OfflineState.APPROVE.equals(OfflineState.values()[mTransRecord.getOfflineState()])) {
            //FF26不可预知数IC卡脱机消费交易时出现
            Optional.ofNullable(getValue(mTransRecord.getResultTlv(), "9F37")).ifPresent(s -> formTlv(sb, "FF26", ByteUtils.hexString2ByteArray(s)));
            //FF27应用交互特征IC卡脱机消费交易时出现
            Optional.ofNullable(getValue(mTransRecord.getResultTlv(), "82")).ifPresent(s -> formTlv(sb, "FF27", ByteUtils.hexString2ByteArray(s)));
            //FF28  终端验证结果  IC卡脱机消费交易时出现
            Optional.ofNullable(getValue(mTransRecord.getResultTlv(), "95")).ifPresent(s -> formTlv(sb, "FF28", ByteUtils.hexString2ByteArray(s)));
            //FF29  交易状态信息	IC卡脱机消费交易时出现
            Optional.ofNullable(getValue(mTransRecord.getResultTlv(), "9B")).ifPresent(s -> formTlv(sb, "FF29", ByteUtils.hexString2ByteArray(s)));
            //FF2A  应用交易计数器	C，IC卡脱机消费交易时出现
            Optional.ofNullable(getValue(mTransRecord.getResultTlv(), "9F36")).ifPresent(s -> formTlv(sb, "FF2A", ByteUtils.hexString2ByteArray(s)));
            //FF2B  发卡应用数据  B,32     IC卡脱机消费交易时出现
            Optional.ofNullable(getValue(mTransRecord.getEmvTlv(), "9F10")).ifPresent(s -> formTlv(sb, "FF2B", ByteUtils.hexString2ByteArray(s)));
        }
        //三、创新业务信息（以下域均指原交易请求报文）
        String str = null;
        if (!TextUtils.isEmpty(mTransRecord.getIssInfo())) {
            str = mTransRecord.getIssInfo();
            if (!TextUtils.isEmpty(mTransRecord.getCupInfo())) {
                str += mTransRecord.getCupInfo();
                if (!TextUtils.isEmpty(mTransRecord.getMerAcqInfo())) {
                    str += mTransRecord.getMerAcqInfo();
                }
            }
        }
        Optional.ofNullable(str).ifPresent(s -> formTlv(sb, "FF40", ByteUtils.hexString2ByteArray(s)));
        //FF41      分期付款期数      Cn,1        分期付款消费交易请求  C，原交易应答返回时出现，包含两位数字
        //FF42      分期付款首期金额        Cn,6        分期付款消费交易应答  C，原交易应答返回时出现，包含12位数字
        //FF43      分期付款还款币种        Cn,2        分期付款消费交易应答  C，原交易应答返回时出现，包含3位数字
        //FF44      持卡人手续费      Cn,6        分期付款消费交易应答  C，原交易应答返回时出现,包含12位数字
        //FF45      商品代码        Ans，var.up.to 30      积分消费交易请求    C，原交易应答返回时出现
        //FF46      兑换积分数	    Cn,5        积分消费交易应答    C，原交易应答返回时出现，包含10位数字
        //FF47      积分余额数	    Cn,5        积分消费交易应答    C，原交易应答返回时出现，包含10位数字
        //FF48      自付金额        Cn,6        积分消费交易应答    C，部分承兑时出现包含12位数字
        //FF49  承兑金额    cN,6    部分承兑交易应答报文第4域	C，部分扣款时出现
        //FF4A  可用余额    cn,6    54域	C，原交易应答返回时出现
        Optional.ofNullable(mTransRecord.getBalance())
                .map(s -> StringUtils.leftPad(s, 12, '0'))
                .ifPresent(s -> formTlv(sb, "FF4A", ByteUtils.hexString2ByteArray(s)));
        //FF4B  手机号码    an,11   62域	C，原交易应答返回时出现，手机号码屏蔽，除前三位和后三位以外全部显示"*"
        //四、原交易信息（以下域均指原交易请求报文）
        //FF60  原凭证号    Cn,3    61域	C，撤销类交易出现  000001000034
        Optional.ofNullable(mTransRecord.getOrgTraceNum()).ifPresent(s -> formTlv(sb, "FF60", ByteUtils.hexString2ByteArray(s)));
        //FF61  原批次号    Cn,3    61域	C，撤销类交易出现
        Optional.ofNullable(mTransRecord.getOrgBatchNum()).ifPresent(s -> formTlv(sb, "FF61", ByteUtils.hexString2ByteArray(s)));
        //FF62  原参考号    Cn,6    37域	C，退货类（不含电子现金退货）交易出现
        Optional.ofNullable(mTransRecord.getOrgRefNum()).ifPresent(s -> formTlv(sb, "FF62", ByteUtils.hexString2ByteArray(s)));
        //FF63  原交易日期	Cn,2    61域	C，退货类（不含电子现金退货）交易出现
        Optional.ofNullable(mTransRecord.getOrgDate()).ifPresent(s -> formTlv(sb, "FF63", ByteUtils.hexString2ByteArray(s)));
        //FF64  原授权码    aN,6    38域	C，预授权撤销、预授权完成（请求）、预授权完成（请求）撤销时输入的原预授权码
        Optional.ofNullable(mTransRecord.getOrgAuthCode())
                .map(s -> ByteUtils.byteArray2HexString(s.getBytes()))
                .ifPresent(s -> formTlv(sb, "FF64", ByteUtils.hexString2ByteArray(s)));
        //FF65  原终端号    Ans，8 62域	C，电子现金退货上送
        //FF70  当前交易打印张数    CN，1  终端  C，若终端获得时上送，表明终端侦测到当笔签字完成后POS终端打印的张数
        Optional.ofNullable(mRepository.getParamValue(PreferencesConstants.PRINT_TIMES))
                .ifPresent(s -> formTlv(sb, "FF70", ByteUtils.hexString2ByteArray("0" + s)));
        //五、保留信息（以下域均指原交易应答报文）
        //FF80  发卡方保留域  ANS20   63.2.1  终端不上送
        //FF81  中国银联保留域	ANS20 63.2.2  终端不上送
        //FF82  受理机构保留域	ANS20 63.2.3  终端不上送
        return sb.toString();
    }

    private String formField60() {
        StringBuilder sb = new StringBuilder();
        sb.append(mTransData.getTransType().getFuncCode());
        sb.append(StringUtils.firstNonEmpty(mTransRecord.getBatchNum(), mRepository.getParamValue(PreferencesConstants.BATCH_NUMBER)));
        sb.append("800");
        return ByteUtils.byteArray2HexString(sb.toString().getBytes());
    }

    private void formTlv(StringBuilder sb, String tag, byte[] value) {
        log.debug("tag {},value {}", tag, ByteUtils.byteArray2HexString(value));
        if (value == null || value.length == 0) {
            sb.append(tag).append("00");
            return;
        }
        int valueLen = value.length;
        sb.append(tag);
        if (valueLen > 255) {
            sb.append("82");
            sb.append(String.format(SysConstant.LOCALE, "%02d", valueLen >> 8));
        } else if (valueLen > 127) {
            sb.append("81");
        }
        sb.append(String.format(SysConstant.LOCALE, "%s", StringUtils.leftPad(Integer.toHexString(valueLen), 2, '0')));
        sb.append(ByteUtils.byteArray2HexString(value));
    }

    private String getValue(String tlv, String tag) {
        if (TextUtils.isEmpty(tlv) || TextUtils.isEmpty(tag)) return null;
        List<String> values = TlvUtils.decodingTLV(tlv, tag);
        return values.size() > 0 ? values.get(0) : null;
    }
}
